package com.extremelylargepackage.entity;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import com.extremelylargepackage.GameResources;
import com.extremelylargepackage.StatTracker;
import com.extremelylargepackage.sprite.AbstractSprite;
import com.extremelylargepackage.sprite.Animation;
import com.extremelylargepackage.sprite.CollidableSprite;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;

public abstract class Entity implements Serializable
{
	protected float _locX, _locY;
	protected float _dirX, _dirY;
	protected float _speed;
	protected transient AbstractSprite _sprite;
	protected transient Status _status;
	protected String _name;
	protected transient Animation _explosionAnim;

	public Entity()
	{
		_locX = _locY = _dirX = _dirY = _speed = 0;
		_status = Status.alive;
		_sprite = null;
	}

	public enum Status{dying, alive, dead, scenery}

	public float getLocX()
	{
		return _locX;
	}

	public float getLocY()
	{
		return _locY;
	}

	public int getWidth()
	{
		return _sprite.getWidth();
	}

	public int getHeight()
	{
		return _sprite.getHeight();
	}

	public float getDirX()
	{
		return _dirX;
	}
	
	public float getDirY()
	{
		return _dirY;
	}
	
	public float getSpeed()
	{
		return _speed;
	}
	
	public Status getStatus()
	{
		return _status;
	}
	
	public String getName()
	{
		return _name;
	}
		
	public void setSpeed(float newSpeed)
	{
		_speed = newSpeed;
	}
		
	public void setDirX(float newDirX)
	{
		_dirX = newDirX;
	}
		
	public void setDirY(float newDirY)
	{
		_dirY = newDirY;
	}

	public void setLocX(float newLocX)
	{
		_locX = newLocX;
	}
		
	public void setLocY(float newLocY)
	{
		_locY = newLocY;
	}
	
	public AbstractSprite getSprite()
	{
		return _sprite;
	}

	public abstract void updateState(long time);
	
	protected void onDying(long time)
	{
		if(_explosionAnim != null) 
		{
			_explosionAnim.update(time);
			if(_explosionAnim.finished()) 
			{
				markDead();
			}
		}
		else
		{
			markDead();
		}
	}
	
	protected void markDead()
	{
		_status = Status.dead;
		if(this instanceof Enemy) 
		{
			StatTracker.getInstance().RegisterKill((Enemy)this);
		}
	}
	
	public abstract void onCollision(Entity z);

	// shouldn't necessarily be abstract? we have all we need in most cases I think
	public abstract void draw(Canvas c);
	
	protected void drawExplosion(Canvas c)
	{
		Bitmap explosion = _explosionAnim.getBitmap();
		float xOffset = (_sprite.getWidth() - explosion.getWidth()) / 2f;
		float yOffset = (_sprite.getHeight() - explosion.getHeight()) / 2f;
		c.drawBitmap(_explosionAnim.getBitmap(), _locX + xOffset, _locY + yOffset, null);
	}

	/**
	 * Determines whether or not this Entity is colliding with the specified Entity e.
	 * @param e
	 * @return
	 */
	public boolean collidesWith(Entity e) {
		if(this._sprite instanceof CollidableSprite && e._sprite instanceof CollidableSprite) {
			return ((CollidableSprite)this._sprite).collidesWith(_locX, _locY,
						(CollidableSprite)e._sprite, e._locX, e._locY);
		} else {
			return false;
		}
	}
	
	protected void move(long time)
	{
		if(_dirX != 0 || _dirY != 0)
		{
			float magnitude = (float)Math.sqrt((_dirX*_dirX) + (_dirY*_dirY));
			float maxMagnitude = _speed * (time/1000f);
			float dx = _dirX;
			float dy = _dirY;
			if(magnitude > maxMagnitude)
			{
				dx *= (maxMagnitude / magnitude);
				dy *= (maxMagnitude / magnitude);
			}
			_locX += dx;
			_locY += dy;
			if(_status != Status.dying)
			{
				_dirX -= dx;
				_dirY -= dy;
			}
		}
	}

	/**
	 * Generates a map of values representing the state of the entity. Ex { (health, 100), (x, 100), (y, 250) }
	 * @return state
	 */
	public Map<String, Object> getState()
	{
		Map<String, Object> res = new HashMap<String, Object>();

		res.put("locX", String.valueOf(_locX));
		res.put("locY", String.valueOf(_locX));
		res.put("dirX", String.valueOf(_dirX));
		res.put("dirY", String.valueOf(_dirX));
		res.put("speed", String.valueOf(_speed));
		res.put("status", _status.toString());
		res.put("name", _name);

		return res;
	}
}